package com.travel.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.druid.support.json.JSONUtils;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.travel.config.factory.AsyncScheduleThreadFactory;
import com.travel.config.redis.BloomFilterRunnable;
import com.travel.context.UserContext;
import com.travel.dto.*;
import com.travel.exception.BusinessException;
import com.travel.mapper.TravelProductMapper;
import com.travel.pojo.TravelAttractions;
import com.travel.pojo.TravelItineraries;
import com.travel.pojo.TravelPricingDetails;
import com.travel.pojo.TravelProducts;
import com.travel.result.PageResult;
import com.travel.vo.AttractionVo;
import com.travel.vo.*;
import com.travel.service.TravelAttractionService;
import com.travel.service.TravelItineraryService;
import com.travel.service.TravelPricingDetailService;
import com.travel.service.TravelProductService;
import jakarta.annotation.PostConstruct;
import lombok.SneakyThrows;
import lombok.Synchronized;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.redisson.client.RedisClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.beans.PropertyDescriptor;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author: libin
 * Date: 2025/6/10 11:16
 * Description:
 **/
@Service
public class TravelProductServiceImpl extends ServiceImpl<TravelProductMapper, TravelProducts> implements TravelProductService {

    @Autowired
    private TravelAttractionService travelAttractionService;

    @Autowired
    private TravelItineraryService travelItineraryService;

    @Autowired
    private TravelPricingDetailService travelPricingDetailService;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private RedissonClient redissonClient;

    @Autowired
    private RBloomFilter rBloomFilter;

    @Autowired
    private TravelProductServiceImpl itemService;

    @PostConstruct   // 在spring创建ItemServiceImplBean对象的生命周期初始化前这一步会来回调。
    // 1.实例化  2.属性填充（自动注入） 3、初始化  4、使用  5、销毁
    public void initBloomFilter() {
        AsyncScheduleThreadFactory.getInstance().execute(new BloomFilterRunnable(stringRedisTemplate, redissonClient, itemService), 0l, TimeUnit.SECONDS);
    }



    @Override
    public TravelProductBasicVo getBasicInfoWithAttractions(Integer productId) {
        // 1. 查询产品基础信息
        TravelProducts product = getById(productId);
        if (product == null) {
            throw new BusinessException("产品不存在");
        }

        // 2. 查询关联景点
        List<TravelAttractions> attractions = travelAttractionService.list(
                new LambdaQueryWrapper<TravelAttractions>()
                        .eq(TravelAttractions::getProductId, productId)
        );

        // 3. 转换为 VO
        TravelProductBasicVo vo = new TravelProductBasicVo();
        BeanUtils.copyProperties(product, vo);
        vo.setAttractions(attractions.stream()
                .map(attraction -> AttractionVo.builder()
                        .id(attraction.getId())
                        .name(attraction.getAttractionName())
                        .desc(attraction.getAttractionDesc())
                        .attractionImage(attraction.getAttractionImage())
                        .build())
                .collect(Collectors.toList()));
        return vo;
    }


    public List<ItineraryDayVo> getItinerariesByDay(Integer productId) {
        // 查询行程并按天数分组
        List<TravelItineraries> itineraries = travelItineraryService.list(
                new LambdaQueryWrapper<TravelItineraries>()
                        .eq(TravelItineraries::getProductId, productId)
                        .orderByAsc(TravelItineraries::getDayNumber) // 按天数升序
        );

        // 转换为按天分组的 VO
        return itineraries.stream()
                .collect(Collectors.groupingBy(TravelItineraries::getDayNumber))
                .entrySet().stream()
                .sorted(Map.Entry.comparingByKey()) // 按天数排序
                .map(entry -> {
                    ItineraryDayVo dayVo = new ItineraryDayVo();
                    dayVo.setDayNumber(entry.getKey());
                    dayVo.setItineraries(entry.getValue().stream()
                            .map(TravelItineraries::getItineraryText)
                            .collect(Collectors.toList()));
                    return dayVo;
                })
                .collect(Collectors.toList());
    }


    @Override
    public List<PricingDetailVo> getPricingDetails(Integer productId) {
        List<TravelPricingDetails> details = travelPricingDetailService.list(
                new LambdaQueryWrapper<TravelPricingDetails>()
                        .eq(TravelPricingDetails::getProductId, productId)
        );
        return details.stream()
                .map(detail -> PricingDetailVo.builder()
                        .itemName(detail.getItemName())
                        .price(detail.getPrice())
                        .isIncluded(detail.getIsIncluded() == 1) // 转换为布尔值
                        .build())
                .collect(Collectors.toList());
    }

    @Override
    public List<String> getCarouselImages() {
        List<TravelProducts> products = list();
        if (CollUtil.isEmpty(products)) {
           return Collections.emptyList();
        }
        List<String> list = products.stream().map(TravelProducts::getImageUrl).toList();
        return CollUtil.distinct(list);
    }

    @Transactional
    @Override
    public Integer addTravelProduct(TravelProductAddDTO dto) {
        // 获取当前用户信息
        Integer currentId = UserContext.getCurrentId();
        TravelProducts travelProducts = BeanUtil.copyProperties(dto, TravelProducts.class);
        travelProducts.setCreateTime(LocalDateTime.now());
        travelProducts.setUpdateTime(LocalDateTime.now());
        travelProducts.setCreateBy(currentId);
        // 保存基本信息
        save(travelProducts);

        Integer productId = travelProducts.getId();
        // 2. 处理关联的行程（travel_itineraries）
        if (CollUtil.isNotEmpty(dto.getItineraries())) {
            List<TravelItineraries> itineraryEntities = dto.getItineraries().stream()
                    .map(itineraryDTO -> {
                        TravelItineraries entity = new TravelItineraries();
                        BeanUtils.copyProperties(itineraryDTO, entity);
                        entity.setProductId(productId); // 关联主表ID
                        entity.setCreateBy(currentId);
                        entity.setCreateTime(LocalDateTime.now());
                        return entity;
                    }).collect(Collectors.toList());
            travelItineraryService.saveBatch(itineraryEntities); // MyBatis Plus 批量插入
        }

        // 3. 处理关联的景点（travel_attractions）
        if (CollUtil.isNotEmpty(dto.getAttractions())) {
            List<TravelAttractions> attractionEntities = dto.getAttractions().stream()
                    .map(attractionDTO -> {
                        TravelAttractions entity = new TravelAttractions();
                        BeanUtils.copyProperties(attractionDTO, entity);
                        entity.setProductId(productId); // 关联主表ID
                        entity.setCreateBy(currentId);
                        entity.setCreateTime(LocalDateTime.now());
                        entity.setLatitude(attractionDTO.getLatitude());
                        entity.setLongitude(attractionDTO.getLongitude());
                        return entity;
                    }).collect(Collectors.toList());
            travelAttractionService.saveBatch(attractionEntities);
        }

        // 4. 处理关联的费用项（travel_pricing_details）
        if (CollUtil.isNotEmpty(dto.getPricingDetails())) {
            List<TravelPricingDetails> pricingEntities = dto.getPricingDetails().stream()
                    .map(pricingDTO -> {
                        TravelPricingDetails entity = new TravelPricingDetails();
                        BeanUtils.copyProperties(pricingDTO, entity);
                        entity.setProductId(productId); // 关联主表ID
                        entity.setCreateBy(currentId);
                        entity.setCreateTime(LocalDateTime.now());
                        return entity;
                    }).collect(Collectors.toList());
            travelPricingDetailService.saveBatch(pricingEntities);
        }
        return productId;
    }


    @Transactional
    @Override
    public boolean updateTravelProduct(TravelProductUpdateDTO dto) {
        // 1. 校验产品是否存在
        TravelProducts existProduct = getById(dto.getId());
        if (existProduct == null) {
            throw new BusinessException("旅游产品不存在，ID: " + dto.getId());
        }

        // 2. 更新主表数据（只更新非空字段）
        Integer userId = UserContext.getCurrentId();
        TravelProducts updateEntity = new TravelProducts();
        BeanUtils.copyProperties(dto, updateEntity, getNullPropertyNames(dto));
        updateEntity.setUpdateBy(userId); // 实际需替换为当前用户
        updateEntity.setUpdateTime(LocalDateTime.now());
        updateById(updateEntity);

        // 3. 处理关联数据
        Integer productId = dto.getId();

        // 3.1 更新行程
        updateItineraries(productId, dto.getItineraries());

        // 3.2 更新景点
        updateAttractions(productId, dto.getAttractions());

        // 3.3 更新费用项
        updatePricingDetails(productId, dto.getPricingDetails());

        return true;
    }

    /**
     * 更新行程信息
     */
    private void updateItineraries(Integer productId, List<TravelItineraryUpdateDTO> itineraries) {
        if (CollUtil.isNotEmpty(itineraries)) {
            // 策略1：删除旧关联，插入新关联（简单但可能影响历史数据）
            travelItineraryService.remove(new QueryWrapper<TravelItineraries>()
                    .eq("product_id", productId));

            // 插入新的行程
            List<TravelItineraries> itineraryEntities = itineraries.stream()
                    .map(itineraryDTO -> {
                        TravelItineraries entity = new TravelItineraries();
                        BeanUtils.copyProperties(itineraryDTO, entity);
                        entity.setProductId(productId);
                        entity.setCreateBy(UserContext.getCurrentId());
                        entity.setCreateTime(LocalDateTime.now());
                        return entity;
                    }).collect(Collectors.toList());
            travelItineraryService.saveBatch(itineraryEntities);
        }
    }

    /**
     * 更新景点信息
     */
    private void updateAttractions(Integer productId, List<TravelAttractionUpdateDTO> attractions) {
        if (CollUtil.isNotEmpty(attractions)) {
            // 删除旧的景点
            travelAttractionService.remove(new QueryWrapper<TravelAttractions>()
                    .eq("product_id", productId));

            // 插入新的景点
            List<TravelAttractions> attractionEntities = attractions.stream()
                    .map(attractionDTO -> {
                        TravelAttractions entity = new TravelAttractions();
                        BeanUtils.copyProperties(attractionDTO, entity);
                        entity.setProductId(productId);
                        entity.setCreateBy(UserContext.getCurrentId());
                        entity.setCreateTime(LocalDateTime.now());
                        entity.setLatitude(attractionDTO.getLatitude());
                        entity.setLongitude(attractionDTO.getLongitude());
                        return entity;
                    }).collect(Collectors.toList());
            travelAttractionService.saveBatch(attractionEntities);
        }
    }

    /**
     * 更新费用项信息
     */
    private void updatePricingDetails(Integer productId, List<TravelPricingDetailUpdateDTO> pricingDetails) {
        if (CollUtil.isNotEmpty(pricingDetails)) {
            // 删除旧的费用项
            travelPricingDetailService.remove(new QueryWrapper<TravelPricingDetails>()
                    .eq("product_id", productId));

            // 插入新的费用项
            List<TravelPricingDetails> pricingEntities = pricingDetails.stream()
                    .map(pricingDTO -> {
                        TravelPricingDetails entity = new TravelPricingDetails();
                        BeanUtils.copyProperties(pricingDTO, entity);
                        entity.setProductId(productId);
                        entity.setCreateBy(UserContext.getCurrentId());
                        entity.setCreateTime(LocalDateTime.now());
                        return entity;
                    }).collect(Collectors.toList());
            travelPricingDetailService.saveBatch(pricingEntities);
        }
    }

    /**
     * 获取对象中为null的属性名
     */
    private String[] getNullPropertyNames(Object source) {
        final BeanWrapper src = new BeanWrapperImpl(source);
        PropertyDescriptor[] pds = src.getPropertyDescriptors();

        Set<String> emptyNames = new HashSet<>();
        for (PropertyDescriptor pd : pds) {
            Object srcValue = src.getPropertyValue(pd.getName());
            if (srcValue == null) emptyNames.add(pd.getName());
        }

        String[] result = new String[emptyNames.size()];
        return emptyNames.toArray(result);
    }

    @Transactional
    @Override
    public boolean deleteTravelProduct(Integer productId) {
        // 1. 校验产品是否存在
        TravelProducts existProduct = getById(productId);
        if (existProduct == null) {
            throw new BusinessException("旅游产品不存在，ID: " + productId);
        }

        // 2. 逻辑删除主表数据（需要在实体类中添加
        removeById(productId);

        // 3. 逻辑删除关联表数据（可选）
        // 方式1：依赖数据库级联删除（需在表结构中设置外键级联）
        // 方式2：手动删除关联表数据
        travelItineraryService.remove(new QueryWrapper<TravelItineraries>()
                .eq("product_id", productId));
        travelAttractionService.remove(new QueryWrapper<TravelAttractions>()
                .eq("product_id", productId));
        travelPricingDetailService.remove(new QueryWrapper<TravelPricingDetails>()
                .eq("product_id", productId));
        return true;
    }


    @SneakyThrows
    @Override
    public TravelProductVo getProductWithRelations(Integer productId) {

        // 0.定义局部变量
        String cacheKey = "cache:info:" + productId;
        String resultFromRedis = stringRedisTemplate.opsForValue().get(cacheKey);
        String lockKey = "lock:" + UserContext.getCurrentId() + ":" + productId;

        // 1.查询分布式布隆过滤器
        boolean contains = rBloomFilter.contains(productId);

        // 2.如果布隆说没有
        if (!contains) {
            return null;
       }

        // 3. 布隆有，从缓存中查询
        if (!StringUtils.isEmpty(resultFromRedis)) {
            return JSONUtil.toBean(resultFromRedis, TravelProductVo.class);
        }
        // 4.得到一个锁对象
        RLock lock = redissonClient.getLock(lockKey);
        // 5. 加锁(非阻塞 && 续期)
        boolean b = lock.tryLock();
        if (b) {
            try {
                // 6.回源
                TravelProductVo travelProductVoDB = getTravelProductVoDB(productId);
                // 7.为不同的数据设置过期时间
                long ttl = 0;
                if (travelProductVoDB == null) {
                    ttl = 60 * 60 * 12;
                } else {
                    ttl = 60 * 60 * 24 * 7;
                }

                //  8.同步数据
                stringRedisTemplate.opsForValue().set(cacheKey, JSONObject.toJSONString(travelProductVoDB), ttl, TimeUnit.SECONDS);

                // 9.返回数据
                return travelProductVoDB;
            } finally {

                // 10.释放锁（避免死锁，不会续期）
                lock.unlock();
            }
        } else {

            // 11. 抢不到锁的线程
            Thread.sleep(500);
            String result = stringRedisTemplate.opsForValue().get(cacheKey);
            return JSONObject.parseObject(result, TravelProductVo.class);
        }
    }

    private TravelProductVo getTravelProductVoDB(Integer productId) {
        // 1. 查询旅游产品主信息
        TravelProducts product = getById(productId);
        if (product == null) {
            return null;
        }

        // 2. 构建查询条件
        LambdaQueryWrapper<TravelItineraries> itineraryWrapper = Wrappers.<TravelItineraries>lambdaQuery()
                .eq(TravelItineraries::getProductId, productId);

        LambdaQueryWrapper<TravelAttractions> attractionWrapper = Wrappers.<TravelAttractions>lambdaQuery()
                .eq(TravelAttractions::getProductId, productId);

        LambdaQueryWrapper<TravelPricingDetails> pricingWrapper = Wrappers.<TravelPricingDetails>lambdaQuery()
                .eq(TravelPricingDetails::getProductId, productId);

        // 3. 查询关联数据
        List<TravelItineraries> itineraries = travelItineraryService.list(itineraryWrapper);
        List<TravelAttractions> attractions = travelAttractionService.list(attractionWrapper);
        List<TravelPricingDetails> pricingDetails = travelPricingDetailService.list(pricingWrapper);

        // 4. 转换为VO对象
        TravelProductVo travelProductVo = BeanUtil.copyProperties(product, TravelProductVo.class);

        // 转换行程列表
        if (!CollUtil.isEmpty(itineraries)) {
            List<TravelItineraryVo> itineraryVos = itineraries.stream()
                    .map(itinerary -> {
                        TravelItineraryVo vo = new TravelItineraryVo();
                        BeanUtil.copyProperties(itinerary, vo);
                        return vo;
                    })
                    .collect(Collectors.toList());
            travelProductVo.setItineraries(itineraryVos);
        }


        // 转换景点列表
        if (!CollUtil.isEmpty(attractions)) {
            List<TravelAttractionVo> attractionVos = attractions.stream()
                    .map(attraction -> {
                        TravelAttractionVo vo = new TravelAttractionVo();
                        BeanUtil.copyProperties(attraction, vo);
                        return vo;
                    })
                    .collect(Collectors.toList());
            travelProductVo.setAttractions(attractionVos);
        }

        // 转换费用项列表
        if (!CollUtil.isEmpty(pricingDetails)) {
            List<TravelPricingDetailVo> pricingVos = pricingDetails.stream()
                    .map(detail -> {
                        TravelPricingDetailVo vo = new TravelPricingDetailVo();
                        BeanUtil.copyProperties(detail, vo);
                        return vo;
                    })
                    .collect(Collectors.toList());
            travelProductVo.setPricingDetails(pricingVos);
        }

        return travelProductVo;
    }

    private TravelProducts getTravelProductDb(Integer productId) {
        TravelProducts product = getById(productId);
        if (product == null) {
            return null;
        }
        return product;
    }

    @Override
    public PageResult getTravelProductList(TravelProductPageDto dto) {
        // 构建查询条件：按创建时间降序
        LambdaQueryWrapper<TravelProducts> wrapper = new LambdaQueryWrapper<>();
        wrapper.orderByDesc(TravelProducts::getCreateTime);

        // 添加模糊查询条件
        if (StrUtil.isNotBlank(dto.getName())) {
            wrapper.like(TravelProducts::getName, dto.getName());
        }

        if (StrUtil.isNotBlank(dto.getLocation())) {
            wrapper.like(TravelProducts::getLocation, dto.getLocation());
        }

        // 分页参数
        IPage<TravelProducts> page = new Page<>(dto.getPageNum(), dto.getPageSize());
        page(page, wrapper);

        // 转换为VO对象
        List<TravelProductVo> productVos = page.getRecords().stream()
                .map(product -> BeanUtil.copyProperties(product, TravelProductVo.class))
                .collect(Collectors.toList());

        return new PageResult(page.getTotal(), productVos);

    }

    @Override
    public List<Integer> getProductIds() {
        LambdaQueryWrapper<TravelProducts> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.select(TravelProducts::getId);
        return list(queryWrapper).stream().map(TravelProducts::getId).toList();
    }
}

